package core

import (
	"fmt"
	"go-fiber-ip2region/global"
	"log"
	"os"
	"reflect"

	"github.com/spf13/viper"
)

func Viper(path ...string) *viper.Viper {
	v := viper.New()

	// 启用自动找环境变量的功能，此时在终端export环境变量，优先级将大于配置文件
	v.AutomaticEnv()

	// 校验`.env`文件是否存在，不存在的话校验`.env.development`文件是否存在
	envFile := "./.env"

	_, exitFileErr := os.Lstat(envFile)
	if os.IsNotExist(exitFileErr) {
		envFile = "./.env.development"
	}

	// 设置读取的配置文件
	v.SetConfigName(envFile)
	// 设置读取配置文件路径
	v.AddConfigPath("./")
	// 设置配置文件类型
	v.SetConfigType("env")

	if err := v.ReadInConfig(); err != nil {
		fmt.Printf("config file reload err:%s\n", err)
	}

	{ // 如果env文件没有版本相关内容，则写入develop
		if v.GetString("version") == "" {
			v.Set("VERSION", "develop")
		}
		if v.GetString("build_at") == "" {
			v.Set("BUILD_AT", "develop")
		}

	}

	// 将环境变量存入 global.GLOBAL_CONFIG 用于全局使用
	// TODO 如何使用 env 文件生成的环境变量 按不同类别分类
	// if err := v.Unmarshal(&global.GLOBAL_CONFIG); err != nil {
	// 	fmt.Println(err)
	// }

	// t := reflect.TypeOf(&global.GLOBAL_CONFIG)
	// if t.Kind() == reflect.Ptr {
	// 	t = t.Elem()
	// }
	// if t.Kind() != reflect.Struct {
	// 	log.Println("check type error: Not Strcut")
	// }

	// fieldNum := t.NumField()
	// result := make([]string, 0, fieldNum)
	// for i := 0; i < fieldNum; i++ {
	// 	result = append(result, t.Field(i).Name)
	// }

	// fmt.Printf("TTT %#v\n", result)

	// for i, val := range result {
	// 	fmt.Printf("%#v\n %#v\n", i, val)

	// 	reflect.ValueOf(&global.GLOBAL_CONFIG).Elem().Field(i).SetString(v.GetString(val))
	// }
	// 自封装
	viperDecode(&global.GLOBAL_CONFIG, v)

	return v
}

// 通过reflect获取struct对应环境变量
func viperDecode(s interface{}, v *viper.Viper) {
	t := reflect.TypeOf(s)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	if t.Kind() != reflect.Struct {
		log.Println("check type error: Not Strcut")
	}

	fieldNum := t.NumField()
	result := make([]string, 0, fieldNum)
	for i := 0; i < fieldNum; i++ {
		result = append(result, t.Field(i).Name)
	}

	for i, val := range result {
		reflect.ValueOf(s).Elem().Field(i).SetString(v.GetString(val))
	}
}
